import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Jeu2 extends JPanel {
	private final int HEIGHT_DEF = 5;
	private int width_def;

	private Plateau plateau;
	private List<Piece> pieces;
	private Algorithme algo;
	private JLabel possibilite;

	// Booleen pour gerer le deplacement des pieces
	private boolean canDrag;
	// Gere deplacement pieces
	private int originX, originY;
	private int dragFromX, dragFromY;
	private Piece dragPiece;
	private boolean canDrop;
	
	// Booleen indiquant si c'est de deplacement d'une piece qui avait déjà été
	// placée sur le plateau
	private boolean repositionning;
	private int[] oldPosition;
	
	static Dimension[] POSITION_PIECE = { new Dimension(0, 0), new Dimension(100, 0), new Dimension(200, 0), new Dimension(300, 0),
									new Dimension(400, 0), new Dimension(300, 105),	new Dimension(400, 105), new Dimension(300, 205),
									new Dimension(400, 205), new Dimension(200, 305), new Dimension(300,305), new Dimension(400, 305)};

	public Jeu2(int taille) {
		width_def = taille;
		createModel();
		createView();
		createController();
		updatePossibilite();
	}

	private void createModel() {
		pieces = new ArrayList<Piece>();
		algo = new Algorithme(HEIGHT_DEF, width_def);

		for (int i = 0; i < Forme.values().length; i++) {
			pieces.add(new Piece(Forme.values()[i], POSITION_PIECE[i].width, POSITION_PIECE[i].height));
		}
	}

	private void createView() {
		plateau = new Plateau(HEIGHT_DEF, width_def);
		possibilite = new JLabel();
		this.setLayout(new BorderLayout());
		this.setPreferredSize(new Dimension(500, 450));
		JPanel p = new JPanel(); {
			p.add(new JLabel("Possibilité restante : "));
			p.add(possibilite);
		}
		this.add(p, BorderLayout.SOUTH);
	}

	private void updatePossibilite() {
		possibilite.setText("" + algo.getNBPossibilite());
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		plateau.paintComponent(g);

		for (Piece p : pieces) {
			p.paintComponent(g);
		}
	}

	private void createController() {
		this.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if (canDrag) {
					dragPiece.prochainSens();
					repaint();
				}
			}
			
		});
		
		this.addMouseListener(new MouseAdapter() {
			/** MOUSE PRESSED **/
			public void mousePressed(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					int x = e.getX();
					int y = e.getY();
	
					// Test toutes les pieces pour savoir si on a cliqué sur l'une d'entre elles
					for (Piece p : pieces) {
						
						// Determine si on a cliqué sur une pièce
						boolean isOnPiece = false;

						// Vérifie d'abord si on est dans le champ d'au moins une des pièces
						if(x >= p.getX() && x <= p.getX() + Piece.WIDTH 
						    && y >= p.getY() && y <= p.getY() + Piece.HEIGHT ) {
						
							// Vérifie qu'on clique sur l'un des carrés de la pièce
							for (Rectangle r : p.getRects()) {
								if (r.contains(e.getPoint())) {
									isOnPiece = true;
									break;
								}
							}
						}
						
						if (isOnPiece) {
							// On stock les coordonnées de depart
							originX = p.getX();
							originY = p.getY();

							// Variables pour déplacer la pièce
							canDrag = true;
							dragFromX = x - originX;
							dragFromY = y - originY;
							dragPiece = p;
	
							// Si c'est une piece qui avait déjà était posée, on en prend note
							if (plateau.getPieces().contains(dragPiece)) {
								plateau.getPieces().remove(dragPiece);
								repositionning = true;
								oldPosition = computeCoordinate(dragPiece);
							}
								
							break;
						} else {
							canDrag = false;
						}
					}
				}
			}

			/** MOUSE RELEASED **/
			public void mouseReleased(MouseEvent e) {
				if (canDrag) {
					canDrop = true;
					
					// Test si on essaie de le poser sur le plateau
					for(Rectangle r : dragPiece.getRects()) {
						boolean isOnPlateau = false;
						for(Rectangle r2 : plateau.getRects()) {
							if(r.intersects(r2)){
								isOnPlateau = true;
							}
						}
						if (!isOnPlateau) {
							canDrop = false;
						}
					}
					
					// Ajuste la position de la piece sur les cases du tableau
					plateau.placePiece(dragPiece);

					// Test si on ne le pose pas où une autre pièce est déjà placée
					for(Piece p : plateau.getPieces()){
						if(p.croiseAutrePiece(dragPiece)){
							canDrop = false;
						}
					}


					// Dépose la pièce, ou la redessine au point de départ
					if (!canDrop) {
						dragPiece.setX(originX);
						dragPiece.setY(originY);
						repaint();
					} else {
						dragPiece.setX(e.getX());
						dragPiece.setY(e.getY());
						
						if (repositionning) {
							reposition();
						}
						addPiece(dragPiece);
						
						
						repaint();
					}
					canDrag = false;
				}
			}
		});

		this.addMouseMotionListener(new MouseMotionListener() {
			public void mouseMoved(MouseEvent e) {
				// Non impl�ment�
			}

			/** MOUSE DRAGGED **/
			public void mouseDragged(MouseEvent e) {
				if (canDrag) {
					// Met a jour les coordonn�es de la pi�ce
					int newX = (int) (e.getX() - dragFromX);
					int newY = (int) (e.getY() - dragFromY);

					dragPiece.setX(newX);
					dragPiece.setY(newY);
				}

				repaint();
			}
		});
	}

	private void addPiece(Piece p) {
		// Ajoute la pièce au plateau
		plateau.addPiece(dragPiece);

		// Transforme les coordonnées x,y de la piece en cases du tableau
		int[] tab = computeCoordinate(p);
		
	    // Prévient l'algo que des cases ont été remplies
		algo.ajoutPiece(tab);

		updatePossibilite();
	}
	
	private void reposition() {	
		// Prévient l'algo que des cases sont redevenues libre
		algo.supprimerPiece(oldPosition);
	}
	
	private int[] computeCoordinate(Piece p) {
		ArrayList<Integer> cases = new ArrayList<Integer>();
		for (int i = 0; i < p.getRects().size(); i++) {
			int x = p.getRects().get(i).x / Plateau.SIZE_CARRE;

			int y = (p.getRects().get(i).y - 120);
			if (y < 0) { y = 0; }
			y = y / Plateau.SIZE_CARRE;

			cases.add(x + width_def * y);

		}

		int[] tab = new int[cases.size()];
	    for (int i=0; i < tab.length; i++)
	    {
	    	tab[i] = cases.get(i).intValue();
	    }

	    return tab;
	}
}
